// Package websocket implements the server side of the gocase protocol. With help from other packages of course.
package websocket

import (
	"http"
	"websocket"
	"container/list"
	"strings"
	"fmt"
	"gocase/db"
	"gocase/validate"
)

type worldMessage struct {
	c   *Client
	msg interface{}
}

var Clients = list.New()
var wmChan = make(chan worldMessage)

func handler(ws *websocket.Conn) {
	c := newClient(ws)
	elem := Clients.PushFront(c)
	c.handle()
	Clients.Remove(elem)
	c.close()
}

func Iterate(f func(c *Client) bool) {
	for e := Clients.Front(); e != nil; e = e.Next() {
		c := e.Value.(*Client)
		if f(c) {
			break
		}
	}
}

func Find(f func(c *Client) bool) (c *Client) {
	Iterate(func(c2 *Client) bool {
		if f(c2) {
			c = c2
			return true
		}
		return false
	})
	return
}

func FindByNickname(nm string) *Client {
	nm = strings.ToLower(nm)
	return Find(func(c *Client) bool {
		return (c.Online && strings.ToLower(c.Nickname) == nm)
	})
}

func Serve() {
	http.Handle("/gocase", websocket.Handler(handler))
	go http.ListenAndServe(":7070", nil)
	world()
}

func world() {
	for {
		wm := <-wmChan
		c := wm.c
		switch t := wm.msg.(type) {
		case *msgJoin: handleJoin(c, t)
		}
	}
}

func handleJoin(c *Client, msg *msgJoin) {
	if c.Online {
		// The client instanly disables the join button once clicked.
		// This crime must be reported!
		return
	}
	
	nm := msg.Nickname
	error := validate.Nickname(nm)
	if error == "" {
		if FindByNickname(nm) != nil {
			error = fmt.Sprintf("%s is occupied.", nm)
		} else {
			if !db.AddUser(nm) {
				db.UpdateUser(nm)
			}
			c.Nickname = nm
			c.Online = true
		}
	}
	c.send(&joinResultMsg{Error: error})
}
